package com.fc.push.handler.pub.service;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;

import org.springframework.stereotype.Component;

import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.fc.commons.security.MD5Util;
import com.fc.push.ServerConfig;
import com.fc.push.exception.MServerException;
import com.fc.push.handler.pub.exec.PublishTaskPool;
import com.fc.push.handler.pub.exec.task.APNsPublishTask;
import com.fc.push.logger.Applogger;

import javapns.communication.exceptions.KeystoreException;
import javapns.notification.AppleNotificationServer;
import javapns.notification.AppleNotificationServerBasicImpl;
import javapns.notification.Payload;
import javapns.notification.PushNotificationPayload;
@Component
public class APNsPublishUtil {
	// payload消息相关
	private static final String ID = "id";
	private static final String MESSAGE_CONTENT = "content";
	private static final String EXTRAS = "extras";
	private static final String TYPE = "type";
	private static final String APS = "aps";
	private static final String DEFAULT = "default";
	private static final String SOUND = "sound";

	// payload长度
	private static final int DEFAULT_PAYLOAD_LENGTH = 230;
	// payload长度超过之后，切割掉的长度
	private static final int DEFAULT_SUBCHAR_LENGTH = 16;
	// 默认分片大小
	private static int SLICE_SIZE = 1000;

	public APNsPublishUtil() {}
	

	/**
	 * 通过分片推送消息
	 * 
	 * @param certificate
	 *            证书内容
	 * @param password
	 *            证书密码
	 * @param payload
	 *            消息
	 * @param isProduction
	 *            是否是生产环境
	 * @param tokens
	 *            要推送的设备token
	 */
	public void pubMessageBySlice(byte[] certificate, String password, Payload payload, boolean isProduction,
			Object... tokens) {
		int totalSize = tokens.length;

		int slice = totalSize / (SLICE_SIZE + 1) + 1;

		/**
		 * 最大分成处理器核数+1片
		 */
		if (ServerConfig.getMaxThreadSize() * SLICE_SIZE < totalSize) {
			slice = totalSize / ServerConfig.getMaxThreadSize() + 1;
		}
		for (int i = 0; i < slice; i++) {
			int begainIndex = i * SLICE_SIZE;
			int endIndex = begainIndex + SLICE_SIZE;
			if (endIndex > totalSize) {
				int size = totalSize - begainIndex;
				Object[] destObjs = new Object[size];
				System.arraycopy(tokens, begainIndex, destObjs, 0, size);
				PublishTaskPool.Instance.getInstance().submit(new APNsPublishTask(
						getNotificationServer(certificate, password, isProduction), payload, destObjs));
				break;
			}
			int size = endIndex - begainIndex;
			Object[] destObjs = new Object[size];
			System.arraycopy(tokens, begainIndex, destObjs, 0, size);
			PublishTaskPool.Instance.getInstance().submit(
					new APNsPublishTask(getNotificationServer(certificate, password, isProduction), payload, destObjs));
		}
	}

	/**
	 * 通过分片提交推送任务
	 * 
	 * @param certificate
	 *            证书内容
	 * @param password
	 *            证书密码
	 * @param payload
	 *            消息
	 * @param token
	 *            要推送的设备token列表
	 * @param payload
	 *            要推送的消息体
	 */
	public void pubMessageBySlice(byte[] certificate, String password, Set<String> token, Payload payload,
			boolean isProduction) {
		int totalSize = token.size();
		Applogger.instance().getLogger().info(Arrays.toString(token.toArray()));
		int slice = totalSize / (SLICE_SIZE + 1) + 1;

		/**
		 * 最大分成处理器核数+1片
		 */
		if (ServerConfig.getMaxThreadSize() * SLICE_SIZE < totalSize) {
			SLICE_SIZE = totalSize / ServerConfig.getMaxThreadSize() + 1;
		}
		List<String> tokens = new ArrayList<>(token);
		for (int i = 0; i < slice; i++) {
			int begainIndex = i * SLICE_SIZE;
			int endIndex = begainIndex + SLICE_SIZE;
			if (endIndex > totalSize) {
				PublishTaskPool.Instance.getInstance()
						.submit(new APNsPublishTask(getNotificationServer(certificate, password, isProduction),
								tokens.subList(begainIndex, totalSize), payload));
				break;
			}
			PublishTaskPool.Instance.getInstance()
					.submit(new APNsPublishTask(getNotificationServer(certificate, password, isProduction),
							tokens.subList(begainIndex, endIndex), payload));
		}
	}

	/**
	 * 获取推送服务器的server
	 * 
	 * @param certificate
	 *            证书内容
	 * @param password
	 *            证书密码
	 * @param isProduction
	 *            是否生产环境
	 * @return
	 */
	public AppleNotificationServer getNotificationServer(byte[] certificate, String password,
			boolean isProduction) {
		AppleNotificationServer server = null;
		try {
			server = new AppleNotificationServerBasicImpl(getKeyStore(certificate, password), password, isProduction);
		} catch (KeystoreException e) {
			throw new MServerException(e, e.getMessage());
		}
		return server;
	}

	/**
	 * 获取p12证书
	 * 
	 * @param certificate
	 *            证书内容
	 * @param password
	 *            证书密码
	 * @return KeyStore
	 */
	public KeyStore getKeyStore(byte[] certificate, String password) {
		KeyStore ks = null;
		InputStream inputStream = null;
		try {
			ks = KeyStore.getInstance("PKCS12");
			inputStream = new ByteArrayInputStream(certificate);
			// inputStream = new FileInputStream("E:\\imall_push_dev.p12");
			ks.load(inputStream, password.toCharArray());
		} catch (KeyStoreException e) {
			throw new MServerException(e, e.getMessage());
		} catch (NoSuchAlgorithmException e) {
			throw new MServerException(e, e.getMessage());
		} catch (CertificateException e) {
			throw new MServerException(e, e.getMessage());
		} catch (IOException e) {
			throw new MServerException(e, e.getMessage());
		} finally {
			if (inputStream != null) {
				try {
					inputStream.close();
				} catch (IOException e) {
					throw new MServerException(e, e.getMessage());
				}
			}
		}
		return ks;
	}

	/**
	 * 生成客户端推送id
	 * 
	 * @param appkey
	 * @param token
	 * @return
	 */
	public String assClientId(String appkey, String token) {
		return MD5Util.Instance.getInstance().md5TO23Bit(appkey + token);
	}

	/**
	 * 生成IOS Payload
	 * 
	 * @param message
	 * @return
	 * @throws org.json.JSONException
	 */
	public PushNotificationPayload assIOSPayload(JSONObject message) throws org.json.JSONException {
		JSONObject payloadJson = new JSONObject();
		JSONObject apsJson = new JSONObject();
		String msgContent = message.getString(MESSAGE_CONTENT);
		String id = message.getString(ID);
		String type = message.getString(TYPE);
		Object extras = message.get(EXTRAS);
		String sound = DEFAULT;
		JSONObject jExtras = null;
		if(extras instanceof JSONObject){
			jExtras = (JSONObject)extras; 
		}else if(extras instanceof String){
			try{
				jExtras = JSONObject.parseObject(extras.toString());
			}catch(JSONException e){
				throw e;
			}catch(Exception e){
				throw e;
			}
		}
		
		if(jExtras != null){
			if(jExtras.containsKey(SOUND)){
				sound = jExtras.getString(SOUND);
			}
		}
		
		apsJson.put(ID, id);
		apsJson.put(TYPE, type);
		apsJson.put(EXTRAS, extras);
		payloadJson.put(APS, apsJson);
		PushNotificationPayload payload = new PushNotificationPayload(payloadJson.toJSONString());
		payload.addBadge(1);// 消息推送标记数，小红圈中显示的数字。
		payload.addSound(sound);
		int length = payload.getPayload().toString().getBytes().length;
		int limitContentLength = DEFAULT_PAYLOAD_LENGTH - length;
		if (msgContent.getBytes().length > limitContentLength) {
			int subCharLength = (msgContent.getBytes().length - limitContentLength) / 2 + DEFAULT_SUBCHAR_LENGTH;
			if (subCharLength < 0) {
				subCharLength = DEFAULT_SUBCHAR_LENGTH;
			}
			msgContent = msgContent.substring(0, msgContent.length() - subCharLength);
			msgContent = msgContent + "..";
		}
		payload.addAlert(msgContent);
		return payload;
	}
}
